// fgiust
// patched in order to get encoding parsed correctly. setEncoding() was called after the parsing of parameters,
// added a costructor that takes an additiona encoding parameter, set by ServletUnitHttpRequest

package com.meterware.servletunit;

/********************************************************************************************************************
 * $Id: RequestContext.java,v 1.6 2004/10/28 23:50:46 russgold Exp $
 *
 * Copyright (c) 2003, Russell Gold
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 *******************************************************************************************************************/
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;

import javax.servlet.http.HttpServletRequest;

import com.meterware.httpunit.HttpUnitUtils;


/**
 * @author <a href="russgold@httpunit.org">Russell Gold</a>
 */
class RequestContext
{

    private Hashtable _parameters = new Hashtable();

    private Hashtable _visibleParameters;

    private HttpServletRequest _parentRequest;

    private URL _url;

    private byte[] _messageBody;

    private String _messageEncoding;

    RequestContext(URL url)
    {
        this(url, null);
    }

    RequestContext(URL url, String messageEncoding)
    {
        _messageEncoding = messageEncoding;
        _url = url;
        String file = _url.getFile();
        if (file.indexOf('?') >= 0)
            loadParameters(file.substring(file.indexOf('?') + 1) /* urlEncoded */);
    }

    void setParentRequest(HttpServletRequest parentRequest)
    {
        _parentRequest = parentRequest;
        _visibleParameters = null;
    }

    String getRequestURI()
    {
        return _url.getPath();
    }

    String getParameter(String name)
    {
        String[] parameters = (String[]) getParameters().get(name);
        return parameters == null ? null : parameters[0];
    }

    Enumeration getParameterNames()
    {
        return getParameters().keys();
    }

    Map getParameterMap()
    {
        return (Map) getParameters().clone();
    }

    String[] getParameterValues(String name)
    {
        return (String[]) getParameters().get(name);
    }

    final static private int STATE_INITIAL = 0;

    final static private int STATE_HAVE_NAME = 1;

    final static private int STATE_HAVE_EQUALS = 2;

    final static private int STATE_HAVE_VALUE = 3;

    /**
     * This method employs a state machine to parse a parameter query string. The transition rules are as follows: State \
     * text '=' '&' initial: have_name - initial have_name: - have_equals initial have_equals: have_value - initial
     * have_value: - initial initial actions occur on the following transitions: initial -> have_name: save token as
     * name have_equals -> initial: record parameter with null value have_value -> initial: record parameter with value
     */
    void loadParameters(String queryString)
    {
        if (queryString.length() == 0)
            return;
        StringTokenizer st = new StringTokenizer(queryString, "&=", /* return tokens */true);
        int state = STATE_INITIAL;
        String name = null;
        String value = null;

        while (st.hasMoreTokens())
        {
            String token = st.nextToken();
            if (token.equals("&"))
            {
                state = STATE_INITIAL;
                if (name != null && value != null)
                    addParameter(name, value);
                name = value = null;
            }
            else if (token.equals("="))
            {
                if (state == STATE_HAVE_NAME)
                {
                    state = STATE_HAVE_EQUALS;
                }
                else if (state == STATE_HAVE_VALUE)
                {
                    state = STATE_INITIAL;
                }
            }
            else if (state == STATE_INITIAL)
            {
                name = HttpUnitUtils.decode(token, getMessageEncoding());
                value = "";
                state = STATE_HAVE_NAME;
            }
            else
            {
                value = HttpUnitUtils.decode(token, getMessageEncoding());
                state = STATE_HAVE_VALUE;
            }
        }
        if (name != null && value != null)
            addParameter(name, value);
    }

    private void addParameter(String name, String encodedValue)
    {
        String[] values = (String[]) _parameters.get(name);
        _visibleParameters = null;
        if (values == null)
        {
            _parameters.put(name, new String[]{encodedValue});
        }
        else
        {
            _parameters.put(name, extendedArray(values, encodedValue));
        }
    }

    private static String[] extendedArray(String[] baseArray, String newValue)
    {
        String[] result = new String[baseArray.length + 1];
        System.arraycopy(baseArray, 0, result, 0, baseArray.length);
        result[baseArray.length] = newValue;
        return result;
    }

    private Hashtable getParameters()
    {
        if (_messageBody != null)
        {
            loadParameters(getMessageBodyAsString());
            _messageBody = null;
        }
        if (_visibleParameters == null)
        {
            if (_parentRequest == null)
            {
                _visibleParameters = _parameters;
            }
            else
            {
                _visibleParameters = new Hashtable();
                final Map parameterMap = _parentRequest.getParameterMap();
                for (Iterator i = parameterMap.keySet().iterator(); i.hasNext();)
                {
                    Object key = i.next();
                    _visibleParameters.put(key, parameterMap.get(key));
                }
                for (Enumeration e = _parameters.keys(); e.hasMoreElements();)
                {
                    Object key = e.nextElement();
                    _visibleParameters.put(key, _parameters.get(key));
                }
            }
        }
        return _visibleParameters;
    }

    private String getMessageBodyAsString()
    {
        try
        {
            return new String(_messageBody, "iso-8859-1");
        }
        catch (UnsupportedEncodingException e)
        {
            return "";
        }
    }

    void setMessageBody(byte[] bytes)
    {
        _messageBody = bytes;
    }

    public void setMessageEncoding(String messageEncoding)
    {
        _messageEncoding = messageEncoding;
    }

    private String getMessageEncoding()
    {
        return _messageEncoding == null ? HttpUnitUtils.DEFAULT_CHARACTER_SET : _messageEncoding;
    }

}
